﻿//======================================================================
//
//	Cross-Platform XNA Menu System
//  http://cpxms.codeplex.com/
//
//  All code and content licensed under Microsoft Public License (Ms-PL)	
//
//======================================================================
//
//	File:		ImageState.cs
//
//	Author:		MJP
//
//	Date:		04/19/09
//
//======================================================================

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Xml.Serialization;
using System.Drawing.Design;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace CPXMenu
{
    /// <summary>
    /// Used to configure an draw an image for different MenuItem's
    /// </summary>
    [TypeConverter(typeof(ExpandableObjectConverter))]
    public class ImageState : IContentOwner
    {
        private Color backgroundColor = Color.TransparentBlack;
        private string textureName = "";
        private Color mask = Color.White;
        private ContentManager contentManager = null;
        private Texture2D whiteTexture = null;
        private Texture2D imageTexture = null;
        private float transitionTime = 0;
		private SpriteBlendMode spriteBlendMode = SpriteBlendMode.AlphaBlend;
        private int animationColumnns = 1;
        private int animationRows = 1;
        private float animationDelay = 0.17f;
        private int numAnimationFrames = 1;
        private int currentFrame = 0;
        private float currentFrameTime = 0;
        private TextureFilterType filterType = TextureFilterType.Trilinear;
        private SpriteEffects spriteEffects = SpriteEffects.None;
        private MenuItem owner = null;

        /// <summary>
        /// Gets or sets the name of the Texture2D resource to load from
        /// the ContentManager and use as the Texture for drawing.  If set
        /// to null or an empty string, no texture will be used.
        /// </summary>
        [Browsable(true)]
        [Description("The name of the image")]
        [Editor(typeof(ContentPickerEditor<Texture2D>), typeof(UITypeEditor))]
        public string TextureName
        {
            get { return textureName; }
            set
            {
                textureName = value;
                if (!string.IsNullOrEmpty(textureName))
                {
                    if (contentManager != null)
                        imageTexture = contentManager.Load<Texture2D>(textureName);
                    else
                        imageTexture = null;
                }
                else
                    imageTexture = null;
            }
        }

        /// <summary>
        /// Gets or sets the background color of image
        /// </summary>
        [Browsable(true)]
        [Description("The background color of the image")]
        [Editor(typeof(ColorPickerEditor), typeof(UITypeEditor))]
        public Color BackgroundColor
        {
            get { return backgroundColor; }
            set { backgroundColor = value; }
        }

        /// <summary>
        /// Gets or sets the mask to apply to the image when drawing
        /// </summary>
        [Browsable(true)]
        [Description("The mask to apply to the image")]
        [Editor(typeof(ColorPickerEditor), typeof(UITypeEditor))]
        public Color Mask
        {
            get { return mask; }
            set { mask = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating the time it takes to transition to this state
        /// </summary>
        [Browsable(true)]
        [Description("The time it takes to transition to this state")]
        public float TransitionTime
        {
            get { return transitionTime; }
            set { transitionTime = MathHelper.Max(0, value); }
        }

		/// <summary>
		/// Gets or sets a value indicating how the image should be blended with the background
		/// </summary>
		[Browsable(true)]
		[Description("Determines how the image should be blended with the background")]
		public SpriteBlendMode BlendMode
		{
			get { return spriteBlendMode; }
			set { spriteBlendMode = value; }
		}

        /// <summary>
        /// Gets or sets a value indicating whether the image is drawn flipped horizontally or vertically
        /// </summary>
        [Browsable(true)]
        [Description("Controls whether the image is drawn flipped horizontally or vertically")]
        public SpriteEffects SpriteEffect
        {
            get { return spriteEffects; }
            set { spriteEffects = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating the texture filtering to use when drawing the sprite
        /// </summary>
        [Browsable(true)]
        [Description("Determines the texture filtering to use when drawing the sprite")]
        public TextureFilterType FilterType
        {
            get { return filterType; }
            set { filterType = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating the total number of animation frames in the source texture
        /// </summary>
        [Browsable(true)]
        [Description("The total number of animation frames in the source texture")]
        public int NumAnimationFrames
        {
            get { return numAnimationFrames; }
            set { numAnimationFrames = (int)MathHelper.Max(0, value); }
        }

        /// <summary>
        /// Gets or sets a value indicating the number of animation frames packed as columns in the source texture
        /// </summary>
        [Browsable(true)]
        [Description("The number of animation frames packed as columns in the source texture")]
        public int AnimationColumns
        {
            get { return animationColumnns; }
            set { animationColumnns = (int)MathHelper.Max(0, value); }
        }

        /// <summary>
        /// Gets or sets a value indicating the number of animation frames packed as rows in the source texture
        /// </summary>
        [Browsable(true)]
        [Description("The number of animation frames packed as rows in the source texture")]
        public int AnimationRows
        {
            get { return animationRows; }
            set { animationRows = (int)MathHelper.Max(0, value); }
        }

        /// <summary>
        /// Gets or sets a value indicating the delay time between animation frames
        /// </summary>
        [Browsable(true)]
        [Description("The delay time between animation frames")]
        public float AnimationDelay
        {
            get { return animationDelay; }
            set { animationDelay = MathHelper.Max(0.00001f, value); }
        }

		/// <summary>
		/// Gets the texture drawn for the image
		/// </summary>
		[Browsable(false)]
		[XmlIgnore]
		public Texture2D ImageTexture
		{
			get { return imageTexture; }
		}

        /// <summary>
        /// Gets the ContentManager
        /// </summary>
        [Browsable(false)]
        [XmlIgnore]
        public ContentManager Content
        {
            get { return contentManager; }
        }

        /// <summary>
        /// Gets the item that owns this ImageState
        /// </summary>
        [Browsable(false)]
        [XmlIgnore]
        public MenuItem OwnerItem
        {
            get { return owner; }
        }

        /// <summary>
        /// Initializes the ImageState
        /// </summary>
        /// <param name="contentManager">The ContentManager to use for loading resources</param>
        internal void Initialize(ContentManager contentManager, MenuItem ownerItem)
        {
            this.contentManager = contentManager;
            this.owner = ownerItem;
            TextureName = textureName;

			whiteTexture = contentManager.Load<Texture2D>("CPXMenu Content\\Textures\\BlankDiffuse");
        }

        /// <summary>
        /// Creates a duplicate ImageState
        /// </summary>
        /// <returns>The duplicate ImageState</returns>
        internal ImageState Clone()
        {
            return (ImageState)this.MemberwiseClone();
        }

        /// <summary>
        /// Called when owned imported content is removed from the project
        /// </summary>
        /// <param name="contentItem">The item that was removed</param>
        internal void OnOwnedContentRemoved(ImportedContentItem contentItem)
        {
            if (textureName == contentItem.Path)
                TextureName = string.Empty;
        }

        /// <summary>
        /// Creates a string representation of the ImageState
        /// </summary>
        /// <returns>The string representation of the ImageState</returns>
        public override string ToString()
        {
            return this.GetType().Name;
        }

        /// <summary>
        /// Renders the image
        /// </summary>
        /// <param name="dt">The time delta</param>
        /// <param name="spriteBatch">The SpriteBatch to use for drawing</param>
        /// <param name="drawRect">The rectangle (absolute coordinates) to which the image should be drawn</param>
        /// <param name="alpha">A value from 0 to 1 indicating how visible this state should be</param>
        public void Render(float dt, SpriteBatch spriteBatch, Rectangle drawRect, float alpha)
        {
            alpha = MathHelper.Clamp(alpha, 0, 1);

            spriteBatch.Begin(spriteBlendMode, SpriteSortMode.Immediate, SaveStateMode.None);

            if (filterType == TextureFilterType.Point)
            {
                spriteBatch.GraphicsDevice.SamplerStates[0].MagFilter = TextureFilter.Point;
                spriteBatch.GraphicsDevice.SamplerStates[1].MinFilter = TextureFilter.Point;
                spriteBatch.GraphicsDevice.SamplerStates[2].MipFilter = TextureFilter.Point;
            }
            else if (filterType == TextureFilterType.Linear)
            {
                spriteBatch.GraphicsDevice.SamplerStates[0].MagFilter = TextureFilter.Linear;
                spriteBatch.GraphicsDevice.SamplerStates[1].MinFilter = TextureFilter.Linear;
                spriteBatch.GraphicsDevice.SamplerStates[2].MipFilter = TextureFilter.Point;
            }
            else
            {
                spriteBatch.GraphicsDevice.SamplerStates[0].MagFilter = TextureFilter.Linear;
                spriteBatch.GraphicsDevice.SamplerStates[1].MinFilter = TextureFilter.Linear;
                spriteBatch.GraphicsDevice.SamplerStates[2].MipFilter = TextureFilter.Linear;
            }

            Color bgColor = new Color(backgroundColor, (byte)(backgroundColor.A * alpha));
            spriteBatch.Draw(whiteTexture, drawRect, bgColor);

            if (imageTexture != null)
            {
                Color imageMask = new Color(mask, (byte)(mask.A * alpha));

                Rectangle sourceRect = new Rectangle(0, 0, imageTexture.Width, imageTexture.Height);

                if (numAnimationFrames > 1)
                {
                    float frameSizeX = imageTexture.Width / (float)animationColumnns;
                    float frameSizeY = imageTexture.Height / (float)animationRows;

                    sourceRect.Width = (int)frameSizeX;
                    sourceRect.Height = (int)frameSizeY;

                    int currentColumn = currentFrame % animationColumnns;
                    int currentRow = currentFrame / animationColumnns;

                    sourceRect.X = (int)(frameSizeX * currentColumn);
                    sourceRect.Y = (int)(frameSizeY * currentRow);

                    currentFrameTime += dt;
                    while (currentFrameTime >= animationDelay)
                    {
                        currentFrameTime -= animationDelay;
                        currentFrame++;
                    }

                    currentFrame %= numAnimationFrames;
                }
                
                spriteBatch.Draw(imageTexture, drawRect, sourceRect, imageMask, 0, Vector2.Zero, spriteEffects, 0);
            }

            spriteBatch.End();
        }

		/// <summary>
		/// Used to specify what kind of texture filtering should be used when drawing the image
		/// </summary>
        public enum TextureFilterType
        {
            Point = 0,
            Linear = 1,
            Trilinear = 2
        }
    }
}